﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlatformPlayer : MonoBehaviour
{
    public bool IsJumping => State == 1;
    
    public float moveSpeed;
    public float jumpHeight;
    public float accelerateTime;
    public float decelerateTime;
    public float jumpDownGravityFactor;
    public bool isGravityDirectionDown;

    public Vector2 size;
    public Vector2 jumpZone;

    public GameObject passHandlerPrefab;
    
    private int State
    {
        get { return _state; }
        set
        {
            _state = value;
            if (value == 0)
            {
                _fallDistance = Mathf.Abs(transform.position.y - _startFallPositionY);
                TryPass();
            }
            else if (value == 1)
            {
                _fallDistance = 0;
                _startFallPositionY = transform.position.y;
            }
        }
    }

    private bool _jumpFlag;
    [SerializeField]
    private int _state;
    private float _currentSpeed;
    private float _accelerateVelocity;
    private float _decelerateVelocity;
    private bool _hide;
    private bool _active;
    private bool _facingLeft;
    private bool _isPassing;
    [SerializeField]
    private float _fallDistance;
    private float _startFallPositionY;
    private LayerMask GroundLayerMask => ~LayerMask.GetMask("SwitchLine");
    protected float Horizontal => Input.GetAxisRaw("Horizontal");
    public bool Active
    {
        get
        {
            return _active;
        }
        set
        {
            _active = value;
        }
    }

    public bool Hide
    {
        get
        {
            return _hide;
        }
        set
        {
            _hide = value;
            Active = !value;
            _spriteRenderer.enabled = !value;
            _rigidbody.simulated = !value;
        }
    }

    public Vector3 OppositePosition
    {
        get
        {
            int rayNum = 10;
            Raycaster raycaster = new Raycaster(rayNum, _gravityDirectionFactor > 0, transform.position, new Vector2(0, _gravityDirectionFactor),
                size.x, true);
            int noHitCount = 0;
            bool firstHit = true;
            bool first = false;
            
            foreach (var raycastHits in raycaster.RayCast(size.y / 2 + 0.1f, GroundLayerMask))
            {
                bool hit = false;
                foreach (var raycastHit in raycastHits)
                {
                    if (raycastHit.collider && !raycastHit.collider.isTrigger && raycastHit.collider != _collider)
                    {
                        hit = true;
                    }
                }

                if (!first)
                {
                    first = true;
                    firstHit = hit;
                }

                if (!hit)
                {
                    ++noHitCount;
                }
            }

            float deltaX = noHitCount * (size.x / (rayNum - 1)) * (firstHit ? -1 : 1);
            
            return transform.position + new Vector3(deltaX, _gravityDirectionFactor * size.y);
        }
    }

    private float JumpVelocity => Mathf.Sqrt(2 * Mathf.Abs(Physics2D.gravity.y) * jumpHeight) * _reverseGravityDirectionFactor;
    private bool IsOnGround
    {
        get
        {
            var colliders = Physics2D.OverlapBoxAll(new Vector2(transform.position.x, transform.position.y + _gravityDirectionFactor * (size.y / 2 + jumpZone.y / 2)), jumpZone, 0,GroundLayerMask);
            foreach(var collider in colliders)
            {
                if(collider != _collider && !collider.isTrigger)
                {
                    return true;
                }
            }
            return false;
        }
    }
    private Rigidbody2D _rigidbody;
    private Collider2D _collider;
    private Animator _animator;
    private SpriteRenderer _spriteRenderer;
    private int _gravityDirectionFactor => isGravityDirectionDown ? -1 : 1;
    private int _reverseGravityDirectionFactor => isGravityDirectionDown ? 1 : -1;

    void Start()
    {
        _rigidbody = GetComponent<Rigidbody2D>();
        _collider = GetComponent<Collider2D>();
        _animator = GetComponent<Animator>();
        _spriteRenderer = GetComponentInChildren<SpriteRenderer>();
        _active = true;
        SetGravityDirection(isGravityDirectionDown);

        _startFallPositionY = transform.position.y;
        State = 0;
    }
    
    private void FixedUpdate()
    {
        if (State == 0) //在地面
        {
            if (IsOnGround)
            {
                /*bool jumpPressed = Input.GetButton("Jump");

                if (!jumpPressed)
                {
                    _jumpFlag = true;
                }

                if (jumpPressed && _active && _jumpFlag)
                {
                    Jump();
                }*/
            }
            else
            {
                State = 1;
                PlayAnimation("Fall");
            }
        }
        else if (State == 1)//在空中
        {
            if (!Input.GetButton("Jump"))
            {
                _jumpFlag = true;
            }

            FallFix();

            if (isGravityDirectionDown)
            {
                if (_rigidbody.velocity.y < 0 && IsOnGround)
                {
                    State = 0;
                }
            }
            else
            {
                if (_rigidbody.velocity.y > 0 && IsOnGround)
                {
                    State = 0;
                }
            }
        }
    }

    private void FallFix()
    {
        if (_active)
        {
            Raycaster raycaster = new Raycaster(6, false, transform.position, new Vector2(0, _gravityDirectionFactor),
                size.x * 0.6f);
            foreach (var raycastHits in raycaster.RayCast(size.y / 2 + Mathf.Abs(_rigidbody.velocity.y) * Time.fixedDeltaTime, GroundLayerMask))
            {
                foreach (var raycastHit in raycastHits)
                {
                    if (raycastHit.collider && !raycastHit.collider.isTrigger && raycastHit.collider != _collider)
                    {
                        transform.position = new Vector3(transform.position.x,
                            raycastHit.point.y + _reverseGravityDirectionFactor * (size.y / 2));
                        return;
                    }
                }
            }
        }
    }
    
    // Update is called once per frame
    private void Update()
    {
        if (_active)
        {
            HorizontalMove();

            if (Horizontal < 0)
            {
                _facingLeft = true;
            }
            else if(Horizontal > 0)
            {
                _facingLeft = false;
            }

            _animator.SetBool("IsMoving", Horizontal != 0);
        }
        else
        {
            _currentSpeed = Mathf.SmoothDamp(_currentSpeed, 0, ref _decelerateVelocity, decelerateTime);
            _rigidbody.velocity = new Vector2(_currentSpeed, _rigidbody.velocity.y);

            _animator.SetBool("IsMoving", false);
        }
        _animator.SetBool("IsOnGround", IsOnGround);

        _animator.SetBool("IsJumping", State == 1);
        _spriteRenderer.flipX = !_facingLeft;

        if(transform.position.y < -10)
        {
            GameManager.Instance?.Remake();
        }

        if (Input.GetKeyDown(KeyCode.R))
        {
            SwitchGravity();
        }
    }

    private void Jump()
    {
        State = 1;
        _rigidbody.velocity = new Vector2(_rigidbody.velocity.x, JumpVelocity);
        _animator.Play("Jump");
        _jumpFlag = false;
    }

    private void HorizontalMove()
    {
        if (Horizontal > 0)
        {
            _currentSpeed = Mathf.SmoothDamp(_currentSpeed, moveSpeed, ref _accelerateVelocity, accelerateTime);
            _rigidbody.velocity = new Vector2(_currentSpeed, _rigidbody.velocity.y);
        }
        else if (Horizontal < 0)
        {
            _currentSpeed = Mathf.SmoothDamp(_currentSpeed, -moveSpeed, ref _accelerateVelocity, accelerateTime);
            _rigidbody.velocity = new Vector2(_currentSpeed, _rigidbody.velocity.y);
        }
        else
        {
            _currentSpeed = Mathf.SmoothDamp(_currentSpeed, 0, ref _decelerateVelocity, decelerateTime);
            _rigidbody.velocity = new Vector2(_currentSpeed, _rigidbody.velocity.y);
        }
    }

    private bool DetectSwitchLine()
    {
        Raycaster raycaster = new Raycaster(6, false, transform.position, new Vector2(0, _gravityDirectionFactor),
            size.x);
        foreach (var raycastHits in raycaster.RayCast(size.y / 2 + 0.1f, LayerMask.GetMask("SwitchLine")))
        {
            foreach (var raycastHit in raycastHits)
            {
                if (raycastHit.collider)
                {
                    return true;
                }
            }
        }
        
        return false;
    }

    private void SetGravityDirection(bool isDown)
    {
        isGravityDirectionDown = isDown;
        Physics2D.gravity = new Vector2(0, Mathf.Abs(Physics2D.gravity.y) * _gravityDirectionFactor);
        _spriteRenderer.flipY = !isDown;
    }

    private void TryPass()
    {
        if (!_isPassing)
        {
            if (_fallDistance >= 2.8f && DetectSwitchLine())
            {
                PlayerPassHandler passHandler = Instantiate(passHandlerPrefab, Vector3.zero, Quaternion.identity).GetComponent<PlayerPassHandler>();
                _rigidbody.velocity = Vector3.zero;
                _isPassing = true;
                passHandler.StartPass(this, OnPassEnd);
            }
        }
    }

    private void OnPassEnd()
    {
        _isPassing = false;
        int reverse = _spriteRenderer.material.GetInt("_IsReverse");
        _spriteRenderer.material.SetInt("_IsReverse", reverse == 1 ? 0 : 1);
        SwitchGravity();
    }
    
    private void SwitchGravity()
    {
        isGravityDirectionDown = !isGravityDirectionDown;
        _startFallPositionY = transform.position.y;
        SetGravityDirection(isGravityDirectionDown);
    }
    
    public void PlayAnimation(string animation)
    {
        _animator.Play(animation);
    }
}